package com.lagou.boot.core;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Set;

/**
 * @author xingyi
 */
public class SpringApplication {

    private static final Logger logger = LoggerFactory.getLogger(SpringApplication.class);

    public static void run(Class<?> bootClass, String[] args) {

        try {
            Tomcat tomcat = createWebserver();
            tomcat.start();
            Thread awaitThread = new Thread("container") {

                @Override
                public void run() {
                    tomcat.getServer().await();
                    logger.info("tomcat shutdown");
                }

            };
            awaitThread.setDaemon(false);
            awaitThread.start();

            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    tomcat.stop();
                } catch (LifecycleException e) {
                    logger.error("tomcat stop error", e);
                }
            }));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static Tomcat createWebserver() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        Tomcat tomcat = new Tomcat();
        final int port = 8080;
        File baseDir = createTempDir("tomcat", port);
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        tomcat.getHost().setAutoDeploy(false);
        StandardContext context = new StandardContext();
        context.setName("");
        context.setPath("");
        context.setDisplayName("tomcat");
        final File docBase = createTempDir("tomcat-docbase", port);
        context.setDocBase(docBase.getAbsolutePath());

        //当没有web.xml时要添加这个，不然会报启动context未配置好
        context.addLifecycleListener(new Tomcat.FixContextListener());

        //从/META-INFO/services/javax.servlet.ServletContainerInitializer中读取自定义SPI并实例化
        String content = FileUtils.readFileToString(new File(SpringApplication.class.getResource("/META-INFO/services/javax.servlet.ServletContainerInitializer").getFile()), "utf8");
        Class servletContainerInitializerClass = Class.forName(content);
        final ServletContainerInitializer sci = (ServletContainerInitializer) servletContainerInitializerClass.newInstance();

        //通过HandlesTypes注解读取自定义SPI最终委托的自定义WebApplicationInitializer类
        //MyWebApplicationInitializer实现了注解方式的spring容器和mvc容器启动
        HandlesTypes handlesTypes = (HandlesTypes) servletContainerInitializerClass.getAnnotation(HandlesTypes.class);
        Set<Class<?>> waiClassSets = new HashSet<>();
        for (Class<?> aClass : handlesTypes.value()) {
            waiClassSets.add(aClass);
        }
        context.addServletContainerInitializer(sci, waiClassSets);

        tomcat.getHost().addChild(context);
        return tomcat;
    }

    protected static final File createTempDir(String prefix, int port) throws IOException {
        File tempDir = Files.createTempDirectory(prefix + "." + port + ".").toFile();
        tempDir.deleteOnExit();
        return tempDir;

    }
}
